home *** CD-ROM | disk | FTP | other *** search
/ FishMarket 1.0 / FishMarket v1.0.iso / fishies / 301-325 / disk_319 / cnewssrc / cnews.src.lzh / relay / history.c < prev    next >
C/C++ Source or Header  |  1989-06-28  |  8KB  |  322 lines

  1. /*
  2.  * history file bashing
  3.  *
  4.  * B 2.10+ news pulls a dirty (and undocumented) trick and converts
  5.  * message-id's to lower case before using them as keys in the dbm file.
  6.  * We don't; you'll want to fix your news readers accordingly.
  7.  *
  8.  * B 2.10.3+ rnews puts out a leading space before received
  9.  * time if the article contains an Expires: header; tough.
  10.  * C news does this right instead of compatibly.
  11.  *
  12.  * The second history field is really two: time-received and Expires: value,
  13.  * separated by a tilde.  This is an attempt at partial compatibility with
  14.  * B news, in that C expire can cope with B news history files.
  15.  *
  16.  * There is no point to storing seek offsets in network byte order in the
  17.  * dbm file, since dbm files are machine-dependent and so can't be shared
  18.  * by dissimilar machines anyway.
  19.  */
  20.  
  21. #include <stdio.h>
  22. #ifndef AMIGA
  23. #  include <sys/types.h>
  24. #endif /* AMIGA */
  25. #include "libc.h"
  26. #include "news.h"
  27. #include "config.h"
  28. #include "fgetmfs.h"
  29. #include "headers.h"
  30. #include "article.h"
  31. #include "history.h"
  32. #include "msgs.h"
  33.  
  34. #ifdef AZTEC_C
  35. #  define memcpy(d,s,n)    movmem((s),(d),(n))
  36. #endif
  37.  
  38. #define HISTNAME "history"    /* name of the history file in $NEWSCTL */
  39. #define FIELDSEP '\t'
  40. #define SUBFIELDSEP '~'
  41.  
  42. /* give 0 & 2 pretty, SVIDish names */
  43. #ifndef SEEK_SET
  44. #define SEEK_SET 0
  45. #define SEEK_END 2
  46. #endif
  47.  
  48. typedef struct {
  49.     char *dptr;
  50.     int dsize;
  51. } datum;
  52.  
  53. /* private data */
  54. static FILE *fp = NULL;
  55. static char *filename;        /* absolute name of the ascii history file */
  56. static boolean writable;
  57.  
  58. /* libdbm imports */
  59. extern int dbminit(), store();
  60. extern datum fetch();
  61.  
  62. /* forward decls */
  63. FORWARD datum getposhist();
  64. FORWARD void mkhistent(), sanitise(), subsanitise();
  65.  
  66. STATIC void
  67. histname()
  68. {
  69.     if (filename == NULL)
  70.         filename = strsave(ctlfile(HISTNAME));
  71. }
  72.  
  73. /*
  74.  * open the history files: ascii first, then dbm.
  75.  * Try a+ mode first, then r mode, as dbm(3) does nowadays,
  76.  * so that this routine can be used by any user to read history files.
  77.  */
  78. STATIC boolean
  79. openhist()
  80. {
  81.     histname();
  82.     if (fp == NULL) {
  83.         if ((fp = fopenclex(filename, "a+")) != NULL)
  84.             writable = YES;
  85.         else if ((fp = fopenwclex(filename, "r")) != NULL)
  86.             writable = NO;
  87.         /* else fp==NULL and fopenwclex just complained */
  88.  
  89.         if (fp != NULL && dbminit(filename) < 0) {
  90.             /* no luck. dbminit will have just honked */
  91.             (void) nfclose(fp);    /* close ascii file */
  92.             fp = NULL;        /* and mark it */
  93.         }
  94.     }
  95.     return fp != NULL;
  96. }
  97.  
  98. STATIC datum
  99. getposhist(msgid)        /* return seek offset of history entry */
  100. char *msgid;
  101. {
  102.     register char *clnmsgid;
  103.     datum msgidkey, keypos;
  104.  
  105.     msgidkey.dptr = NULL;
  106.     msgidkey.dsize = 0;
  107.     if (!openhist())
  108.         return msgidkey;
  109.     clnmsgid = strsave(msgid);
  110.     sanitise(clnmsgid);
  111.     msgidkey.dptr = clnmsgid;
  112.     msgidkey.dsize = strlen(clnmsgid) + 1;    /* include NUL */
  113.     keypos = fetch(msgidkey);        /* offset into ascii file */
  114.     free(clnmsgid);
  115.     return keypos;
  116. }
  117.  
  118. boolean
  119. alreadyseen(msgid)        /* return true if found in the data base */
  120. char *msgid;
  121. {
  122.     datum posdatum;
  123.  
  124.     posdatum = getposhist(msgid);
  125.     return posdatum.dptr != NULL;
  126. }
  127.  
  128. char *                /* NULL if no history entry */
  129. gethistory(msgid)        /* return existing history entry, if any */
  130. char *msgid;
  131. {
  132.     long pos = 0;
  133.     datum posdatum;
  134.  
  135.     posdatum = getposhist(msgid);
  136.     if (posdatum.dptr != NULL && posdatum.dsize == sizeof pos) {
  137.         static char *histent = NULL;
  138.  
  139.         memcpy((char *)&pos, posdatum.dptr, sizeof pos); /* align */
  140.         nnfree(&histent);
  141.         if (fseek(fp, pos, SEEK_SET) != -1 &&
  142.             (histent = fgetms(fp)) != NULL)
  143.             return histent;
  144.     }
  145.     return NULL;
  146. }
  147.  
  148. /*
  149.  * Return a pointer to the "files" field of a history entry.
  150.  * Side-effect: trims \n from the history entry.
  151.  */
  152. char *
  153. findfiles(histent)
  154. char *histent;
  155. {
  156.     register char *tabp;
  157.  
  158.     trim(histent);
  159.     /* find start of 2nd field (arrival~expiry) */
  160.     tabp = index(histent, FIELDSEP);
  161.     if (tabp == NULL)
  162.         return NULL;                /* mangled entry */
  163.     /* find start of 3rd field (files list) */
  164.     else if ((tabp = index(tabp + 1, FIELDSEP)) == NULL)
  165.         return NULL;            /* cancelled or expired art. */
  166.     else
  167.         return tabp + 1;
  168. }
  169.  
  170. /*
  171.  * Generate a history entry from art.
  172.  * The history entry will have tabs and newlines deleted from the
  173.  * interior of fields, to keep the file format sane.
  174.  * Optionally print the start of an "accepted" log file line (no \n)
  175.  * (transmit() prints site names).
  176.  */
  177. void
  178. history(art, startlog)
  179. register struct article *art;
  180. boolean startlog;
  181. {
  182.     register char *msgid, *expiry;
  183.     time_t now;
  184.  
  185.     msgid = strsave(nullify(art->h.h_msgid));
  186.     sanitise(msgid);    /* RFC 1036 forbids whitespace in msg-ids */
  187.     expiry = strsave(nullify(art->h.h_expiry));
  188.     sanitise(expiry);
  189.     subsanitise(expiry);
  190.  
  191.     if (startlog) {
  192.         timestamp(stdout, &now);
  193.         if (printf(" %s + %s", sendersite(nullify(art->h.h_path)),
  194.             msgid) == EOF)
  195.             fulldisk(art, "stdout");
  196.     } else
  197.         now = time(&now);
  198.     if (!openhist())
  199.         art->a_status |= ST_DROPPED;    /* fall through and return */
  200.     else if (!writable) {
  201.         (void) fprintf(stderr, "%s: no write permission on `%s'\n",
  202.             progname, filename);
  203.         art->a_status |= ST_DROPPED;
  204.     } else if (fseek(fp, 0L, SEEK_END) == -1) {
  205.         warning("can't seek to end of `%s'", filename);
  206.         art->a_status |= ST_DROPPED;
  207.     } else
  208.         mkhistent(art, msgid, now, expiry);
  209.     free(msgid);
  210.     free(expiry);
  211. }
  212.  
  213. /*
  214.  * Internal interface to generate a history file entry,
  215.  * assuming all sanity checking has been done already.
  216.  * Record the (msgid, position) pair in the data base.
  217.  *
  218.  * The fflush is crash-proofing.
  219.  */
  220. STATIC void
  221. mkhistent(art, msgid, now, expiry)
  222. register struct article *art;
  223. char *msgid, *expiry;
  224. time_t now;
  225. {
  226.     long pos;
  227.     datum msgidkey, posdatum;
  228.                                         
  229.     pos = ftell(fp);            /* get seek ptr for dbm */
  230.     if (fprintf(fp, "%s%c%ld%c%s", msgid, FIELDSEP, now, SUBFIELDSEP, expiry)
  231.         == EOF)
  232.         fulldisk(art, filename);
  233.     /* don't write 3rd field for cancelled but unseen articles */
  234.     if (art->a_files != NULL && art->a_files[0] != '\0')
  235.         if (fprintf(fp, "%c%s", FIELDSEP, art->a_files) == EOF)
  236.             fulldisk(art, filename);
  237.     (void) putc('\n', fp);
  238.     if (fflush(fp) == EOF)
  239.         fulldisk(art, filename);
  240.  
  241.     msgidkey.dptr = msgid;
  242.     msgidkey.dsize = strlen(msgid) + 1;    /* include NUL */
  243.     posdatum.dptr = (char *)&pos;
  244.     posdatum.dsize = sizeof pos;
  245. #ifdef NOSTOREVAL
  246.     /* original v7 dbm store() returned no value */
  247.     (void) store(msgidkey, posdatum);
  248. #else
  249.     if (store(msgidkey, posdatum) < 0)
  250.         fulldisk(art, filename);
  251. #endif
  252. }
  253.  
  254. /*
  255.  * Turn \n & FIELDSEP into ' ' in s.
  256.  */
  257. STATIC void
  258. sanitise(s)
  259. register char *s;
  260. {
  261.     for (; *s != '\0'; ++s)
  262.         if (*s == FIELDSEP || *s == '\n')
  263.             *s = ' ';
  264. }
  265.  
  266. /*
  267.  * Turn SUBFIELDSEP into ' ' in s.
  268.  */
  269. STATIC void
  270. subsanitise(s)
  271. register char *s;
  272. {
  273.     for (; *s != '\0'; ++s)
  274.         if (*s == SUBFIELDSEP)
  275.             *s = ' ';
  276. }
  277.  
  278. /*
  279.  * Generate a fake history file entry, given a message-id, an Expires:
  280.  * value, and a "file" list ("net.foo/123").
  281.  */
  282. statust
  283. fakehist(fkmsgid, fkexpiry, fkfiles)
  284. char *fkmsgid, *fkexpiry, *fkfiles;
  285. {
  286.     struct article art;
  287.  
  288.     artinit(&art);
  289.     art.h.h_msgid = fkmsgid;
  290.     art.h.h_expiry = fkexpiry;
  291.     art.a_files = fkfiles;
  292.     history(&art, STARTLOG);
  293.     return art.a_status;
  294. }
  295.  
  296. /*
  297.  * Append "group/artnumstr" to the file list in *art.
  298.  */
  299. void
  300. histupdfiles(art, group, artnumstr)
  301. register struct article *art;
  302. register char *group;
  303. register char *artnumstr;
  304. {
  305.     unsigned addlen = strlen(group)+STRLEN(SFNDELIM)+strlen(artnumstr)+1;
  306.  
  307.     art->a_filed = YES;            /* make a note */
  308.     if (art->a_files == NULL) {
  309.         art->a_files = nemalloc(addlen);
  310.         art->a_files[0] = '\0';
  311.     } else {
  312.         art->a_files = realloc(art->a_files, (unsigned)
  313.             strlen(art->a_files) + STRLEN(" ") + addlen);
  314.         if (art->a_files == NULL)
  315.             errunlock("can't grow a_files", "");
  316.         (void) strcat(art->a_files, " ");
  317.     }
  318.     (void) strcat(art->a_files, group);    /* normal case */
  319.     (void) strcat(art->a_files, SFNDELIM);
  320.     (void) strcat(art->a_files, artnumstr);
  321. }
  322.